#ifndef __OJ_CONTROL_HPP__
#define __OJ_CONTROL_HPP__
// oj控制中心
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <mutex>
#include <fstream>
#include <jsoncpp/json/json.h>

//#include "oj_model_file.hpp"
#include "oj_model_mysql.hpp"
#include "oj_view.hpp"
#include "../common/util.hpp"
#include "../common/log.hpp"
#include "../common/httplib.h"
#include "oj_user.hpp"


namespace ns_control
{
    using namespace ns_model;
    using namespace ns_util;
    using namespace ns_log;
    using namespace ns_view;
    using namespace ns_user;

    class Machine
    {
    public:
        std::string ip;  // 编译服务主机ip
        uint16_t port;  // 编译服务主机端口
    private:
        uint64_t _load;  // 当前主机负载 - 使用个数
        std::mutex* _mtx;  // 当前主机锁，保护对负载数的安全
    public:
        // 构造一个主机对象，给定ip和port。为之后的连接编译服务做准备
        Machine(const std::string& ip_ = "", uint16_t port_ = 0)
        :ip(ip_), port(port_), _load(0), _mtx(nullptr)
        {
            if (ip_ != "" && port_ != 0) _mtx = new std::mutex();
        }
        ~Machine()
        {}

        // 对负载进行递增 - 可能存在多个对象对此主机对象进行操作，必须保证原子性
        void IncLoad()
        {
            if (_mtx)
            {
                _mtx->lock();
                ++_load;
                _mtx->unlock();
            }
        }

        // 对负载进行递减
        void DecLoad()
        {
            if (_mtx)
            {
                _mtx->lock();
                --_load;
                _mtx->unlock();
            }
        }

        // 获取当前主机负载
        uint64_t Load()
        {
            uint64_t load = 0;
            if (_mtx)
            {
                _mtx->lock();
                load = _load;
                _mtx->unlock();
            }
            return load;
        }

        // 清空当前负载
        void ClearLoad()
        {
            if (_mtx)
            {
                _mtx->lock();
                _load = 0;
                _mtx->unlock();
            }
        }
    };

    // 负载均衡模块
    class LoadBlance
    {
        const std::string confPath = "./conf/server_machine.conf";
    private:
        std::vector<Machine> _machines;  // 存放全体主机对象的地方， 包括离线和在线的主机，下标对应主机编号
        std::vector<int> _online;  // 存放在线主机的地方
        std::vector<int> _offline;  // 存放离线主机的地方 
        std::mutex _mtx;// 保证在智能选择或者对数据查询时出现不可重入，保证负载均衡数据安全
    public:
        LoadBlance()
        {
            assert(LoadConf(confPath));
            LOG(INFO) << "编译服务主机配置设置成功..." << "\n";
        }
        ~LoadBlance(){}

        // 加载编译服务主机入内存
        bool LoadConf(const std::string& path_conf)
        {
            std::ifstream in(path_conf);
            if (!in.is_open())
            {
                LOG(FATAL) << "加载主机配置文件失败, 请检查server_machine.conf文件的相关配置路径..." << "\n";
                return false;
            }

            std::string line;  // 一行一行的读取
            while(std::getline(in, line))
            {
                std::vector<std::string> machines;
                StringUtil::SpiltString(line, machines, ":");
                if (machines.size() != 2)
                {
                    LOG(WARING) << "当前行配置文件出错：" << line << "...\n";
                    continue;
                }

                Machine m(machines[0], atoi(machines[1].c_str()));
                // 先加载入在线主机数组
                _online.push_back(_machines.size());
                _machines.push_back(m);
            }

            in.close();
            return true;
        }

        // 负载均衡选择，轮询查询，找到当前负载最小的编译服务主机
        // 参数id为主机编号，m为主机对象，均为输出型参数
        bool SmartChoice(int& id, Machine** m)
        {
            _mtx.lock();
            // 首先检测当前主机是否存在在线
            if (_online.size() == 0)
            {
                // 此时没有一台主机在线
                _mtx.unlock();
                LOG(FATAL) << "请检查后端编译服务主机，当前没有一台在线!..." << "\n";
                return false;
            }

            // 轮询查询
            id = _online[0];
            *m = &_machines[_online[0]];
            uint64_t min_load = _machines[_online[0]].Load();
            for (int i = 1; i < _online.size(); ++i)
            {
                uint64_t load = _machines[_online[i]].Load();
                if (min_load > load)
                {
                    id = _online[i];
                    *m = &_machines[_online[i]];
                    min_load = load;              
                }
            }
            _mtx.unlock();
            return true;
        }

        // 离线指定id主机
        void OfflineMachine(int which)
        {
            // 操作数据的时候，避免线程安全问题，加锁
            _mtx.lock();
            // 从在线列表中退出，加入离线列表
            for (auto it = _online.begin(); it != _online.end(); ++it)
            {
                if (*it == which)
                {
                    // 找到了，退出online，加入offline
                    _online.erase(it);  // 注意删除后存在迭代器失效问题
                    _offline.push_back(which);  // 添加即可
                    break;
                }
            }
            _machines[which].ClearLoad();  // 离线清空负载情况
            _mtx.unlock();
        }

        // 上线主机
        void OnlineMachine()
        {
            // 上线主机默认全部上线
            _online.insert(_online.end(), _offline.begin(), _offline.end());
            _offline.clear();  // 清空
            LOG(INFO) << "当前主机全部上线！" << "\n";
        }

        // debug 展示当前在线主机和离线主机 - compile_run server服务
        void ShowMachines()
        {
            // 访问数据上锁访问哦
            _mtx.lock();
            std::cout << "当前在线主机id: ";
            for (auto i : _online)
            {
                std::cout << i << " ";
            }
            std::cout << std::endl;

            std::cout << "当前离线主机id: ";
            for (auto i : _offline)
            {
                std::cout << i << " ";
            }
            std::cout << std::endl;

            _mtx.unlock();
        }
    };

    class Control
    {
    private:
        Model _model;  // 数据对象
        View _view;  // 渲染对象
        LoadBlance _load_blance;  // 负载均衡控制对象
        User _user;  // 用户管理对象

    public:
        Control()
        {}
        ~Control()
        {}

        // 从题库中获取题目信息(model)，渲染成网页返回(view)
        bool AllQuestions(std::string& html)
        {
            std::vector<Question> v;
            if (_model.GetAllQuestions(v))
            {
                // 对数组进行排序
                std::sort(v.begin(), v.end(), [](Question& q1, Question& q2){
                    return atoi(q1.number.c_str()) < atoi(q2.number.c_str());
                });

                // 成功，根据获取的信息渲染网页
                _view.AllExpandHtml(v, html);
                return true;
            }
            else
            {
                // 获取题目列表失败
                return false;
            }
        }

        // 根据题目编号获取单个题目细节信息，渲染成网页进行返回
        bool OneQuestion(const std::string& number, std::string& html)
        {
            Question q;
            if (_model.GetOneQuestion(number, q))
            {
                // 成功，根据题目文件渲染为网页
                _view.OneExpandHtml(q, html);
                return true;
            }
            else
            {
                // 查无此物
                return false;
            }
        }

        // 重新上线主机
        void AllOnLineMachine()
        {
            _load_blance.OnlineMachine();
        }

        /******************************
         *         判题功能judge
         * 1.number 题目编号
         * 2.in_json 用户提交的json串，包含写过的code代码、以及input输入
         * 3.out_json 最后返回给调用的串，结果为compile_run服务的结果 - 输出型参数
         * 本模块根据配置文件负载均衡选择后端的compile_run服务，实现oj功能
        *******************************/
        void Judge(const std::string& number, const std::string& in_json, std::string& out_json)
        {
            // LOG(DEBUG) << in_json << "\n";
            Question q;
            if (!_model.GetOneQuestion(number, q)) return;

            // 待提交给后端编译服务的json串
            std::string result_json;

            // 1.反序列化
            Json::Value in;
            Json::Reader read;
            read.parse(in_json, in);
            // 2.现在开始序列化 根据题目细节和用户提交的code拼接给后端编译服务提交的json串
            Json::Value out;
            Json::FastWriter write;
            // 拼接代码判断前端是测试运行还是代码提交，代码提交严格执行测试用例的内容，测试运行时根据用户的input结果来
            if (in["test_input"].asBool())
            {
                // 测试代码运行，拼接测试代码
                out["code"] = in["code"].asString() + "\n" + q.test_input;
            }
            else out["code"] = in["code"].asString() + "\n" + q.tail;  // 拼接代码

            out["input"] = in["input"].asString();
            out["cpu_limit"] = q.cpu_commit;
            out["mem_limit"] = q.mem_commit;
            result_json = write.write(out);

            // 3. 负载均衡模块，选择一台服务主机进行发送，接收消息
            // 因为存在选择一台主机发送消息可能得不到消息，所以决定循环式的发送消息，直到主机全部挂掉或者得到消息为止
            while(true)
            {
                int machine_id;
                Machine* m = nullptr;
                if (!_load_blance.SmartChoice(machine_id, &m)) break;  // 此时全体挂掉了

                // 发起http请求
                httplib::Client cli(m->ip, m->port);
                // 注意，请求是需要时间的并且是阻塞式的，同时间内可能会来多个请求，那么需要怎加负载进行访问
                m->IncLoad();
                LOG(INFO) << "主机编号为:" << machine_id << " 详细信息ip:" << m->ip << " port:" << m->port << " 当前负载:" << m->Load() << "\n";
                if (auto res = cli.Post("/compile_and_run", result_json, "application/json;charset=utf-8"))  // 注意res重载了bool，如果没有返回一个响应对象会显示false
                {
                    // 得到响应了
                    // 先判断是不是正确的响应
                    if(res->status == 200)
                    {
                        out_json = res->body;  // post json串在body内
                        m->DecLoad();  // 别忘了减少负载
                        LOG(INFO) << "请求编译运行服务成功..." << "\n";
                        break;
                    }
                }
                else
                {
                    // 没有得到响应，说明此服务器可能挂掉了，加载入离线主机
                    m->DecLoad();
                    LOG(ERROR) << "主机编号:" << machine_id << "详细信息ip:" << m->ip << " port:" << m->port << "可能已经离线....." << "\n";
                    _load_blance.OfflineMachine(machine_id);
                    _load_blance.ShowMachines();
                }
            }
        }

        // 用户管理功能
	// 检查用户权限
    	bool CheckUserIsRoot(const std::string& session_id) {
            std::string username = _user.GetUsernameBySession(session_id);
            return _user.IsRoot(username);
        }

	    bool AdminPage(std::string& html) {
            _view.AdminPageHtml(html);
            return true;
        }

        void AddQuestion(const std::string& in_json, std::string& out_json) {
 	   Json::Value req;
           Json::Reader reader;
    	   if (!reader.parse(in_json, req)) {
	        LOG(ERROR) << "JSON 解析失败\n";
        	return;
   	     }
    	    Question q;
    	    q.number = req["number"].asString();
    	    q.title = req["title"].asString();
    	    q.star = req["star"].asString();      // 难度
    	    q.desc = req["desc"].asString();      // 题目描述
    	    q.header = req["header"].asString();  // 预设代码
    	    q.tail = req["tail"].asString();      // 测试用例拼接代码
    	    q.test_input = req["test_input"].asString();
    	    q.cpu_commit = atoi(req["cpu_limit"].asString().c_str());
    	    q.mem_commit = atoi(req["mem_limit"].asString().c_str());

    	    bool success = _model.AddQuestion(q);
    	    Json::Value res;
    	    res["success"] = success;
    	    out_json = Json::FastWriter().write(res);
        }




        // 登录模块
        void user_control(const std::string& in_json, std::string& out_json, httplib::Response& resp)
        {
            // 先解析in_json
            Json::Value in;
            Json::Reader read;
            read.parse(in_json, in);  // 反序列化
            // 判断是登录还是注册
            int res = 0;
            // 得到用户名和密码
            std::string username = in["username"].asString();
            std::string password = in["password"].asString();
            if(in["login"].asBool())
            {
                if (in["changePassword"].asBool())
                {
                    // 执行修改密码功能
                    LOG(DEBUG) << "对方进行修改密码操作:user:" << username << "\n";
                    // 查看密码是否正确
                    res = _user.FindUser(username, password);
                    if (res == 0)
                    {
                        // 密码正确修改密码
                        std::string newPassword = in["newPassword"].asString();
                        if (_user.ChangePassWord(username, newPassword))
                        {
                            LOG(DEBUG) << "用户修改密码操作成功！:user:" << username << "\n";
                        }
                        else res = -1;
                    }
                }
                else
                {
                    LOG(DEBUG) << "对方发送了登录信息:user:" << username << " password:" << password << "\n";
                    // 执行登录管理功能
                    res = _user.FindUser(username, password);
                    // 第一次登录成功，生成cookie码，并且通过响应对象返回
                    if (res == 0)
                    {
                        std::string session_id = _user.GenerateSessionId();
                        _user._sessions[username] = session_id;

                        // 设置过期时间
                        std::time_t now = std::time(nullptr);
                        std::time_t expire_time = now + 3600; // 3600秒 = 1h
                        // 使用ctime函数将过期时间转换为GMT时间格式
                        char expire_time_str[100];
                        std::strftime(expire_time_str, sizeof(expire_time_str), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&expire_time));


                        resp.set_header("Set-Cookie", "session_id=" + session_id + "; Expires=" + expire_time_str);  // 设置为cookie和过期时间
                        LOG(DEBUG) << "登录成功！发送cookie信息:" << " session_id:" << session_id << "\n";
                    }
                }
            }
            else
            {
                // 注册功能
                LOG(DEBUG) << "对方发送了注册信息:user:" << username << " password:" << password << "\n";
                res = _user.RegisterUser(username, password);
            }
            // 构建json串返回
            Json::Value out;
            Json::FastWriter write;
            out["reason"] = res;
            // 反序列化返回
            out_json = write.write(out);
        }

    };
}

#endif
